#if OPENFLIGHTIMPORT

using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using Microsoft.Xna.Framework.Graphics;
using ShevaEngine.Core.Modules.Models;
using ShevaEngine.Core.Modules.Models.Pipeline;
using ShevaEngine2ContentPipeline.Common;


namespace ShevaEngine2ContentPipeline.Importers
{
    /// <summary>
    /// This class will be instantiated by the XNA Framework Content Pipeline
    /// to import a file from disk into the specified type, TImport.
    /// 
    /// This should be part of a Content Pipeline Extension Library project.
    /// 
    /// TODO: change the ContentImporter attribute to specify the correct file
    /// extension, display name, and default processor for this importer.
    /// </summary>
    [ContentImporter(".flt", DisplayName = "OpenFlight Importer", DefaultProcessor = "ShevaBaseModelProcessor")]
    public class FltImporter : ContentImporter<ModelInternal>
    {
        /// <summary>Vertex palette.</summary>
        private FLTAbstractVertexPalette VertexPalette;
        /// <summary>Texture palette.</summary>
        private Dictionary<int, FLTAbstractTexturePalette> TexturePalettes;

        /// <summary>Buffers.</summary>
        private List<ModelInternalBuffer> Buffers;
        /// <summary>VertexDeclaration to Buffer map.</summary>
        public Dictionary<VertexDeclarationContent, int> VDBuffers;
        /// <summary>Instances.</summary>
        private Dictionary<Int32, IEnumerable<ModelInternalNode>> Instances;


        public override ModelInternal Import(string filename, ContentImporterContext context)
        {
            ModelInternal output = new ModelInternal();

            FLTAbstractBase rootNode = OpenFlightImporter.Import(filename);

            this.Instances = new Dictionary<int, IEnumerable<ModelInternalNode>>();
            this.TexturePalettes = new Dictionary<int, FLTAbstractTexturePalette>();
            
            this.Buffers = new List<ModelInternalBuffer>();
            this.VDBuffers = new Dictionary<VertexDeclarationContent, int>(new VertexDeclarationEqualityComparer());

            ModelInternalNode modelNode = new ModelInternalNode();

            this.ProcessNode(rootNode, modelNode);

            foreach (ModelInternalNode item in modelNode)
            {
                this.ComputeBoundingSphere(item, Matrix.Identity);
                output.Add(item);
            }

            this.Ascii(rootNode, @"C:\Projects\file.txt");

            output.Buffers = this.Buffers;  

            return output;
        }

        /// <summary>
        /// Process node.
        /// </summary>
        /// <param name="rootNode"></param>
        private List<InternalTriangle> ProcessNode(FLTAbstractBase node, ModelInternalNode modelNode)
        {
            List<InternalTriangle> triangles = new List<InternalTriangle>();

            if (node is FLTAbstractGroup)
                triangles.AddRange(this.ProcessGroup(node as FLTAbstractGroup, modelNode));
            else if (node is FLTAbstractLevelOfDetail)
                triangles.AddRange(this.ProcessLOD(node as FLTAbstractLevelOfDetail, modelNode));
            else if (node is FLTAbstractInstanceDefinition)
                this.ProcessInstanceDefinition(node as FLTAbstractInstanceDefinition);
            else if (node is FLTAbstractInstanceReference)
                this.ProcessInstanceReference(node as FLTAbstractInstanceReference, modelNode);
            else if (node is FLTAbstractSwitch)
                triangles.AddRange(this.ProcessSwitch(node as FLTAbstractSwitch, modelNode));
            else if (node is FLTAbstractObject)
                triangles.AddRange(this.ProcessObject(node as FLTAbstractObject, modelNode));
            else if (node is FLTAbstractDegreeOfFreedom)
                triangles.AddRange(this.ProcessDOF(node as FLTAbstractDegreeOfFreedom, modelNode));
            else if (node is FLTAbstractFace)
                triangles.AddRange(this.ProcessFace(node as FLTAbstractFace));
            else if (node is FLTAbstractExternalReference)
                triangles.AddRange(this.ProcessExternalRef(node as FLTAbstractExternalReference, modelNode));
            else if (node is FLTAbstractVertexPalette)
                this.ProcessVertexPalette(node as FLTAbstractVertexPalette);
            else if (node is FLTAbstractTexturePalette)
                this.TexturePalettes.Add((node as FLTAbstractTexturePalette).PatternIndex, node as FLTAbstractTexturePalette);
            else
            {
                foreach (FLTAbstractBase item in node.AncillaryNodes)
                    triangles.AddRange(this.ProcessNode(item, modelNode));

                foreach (FLTAbstractBase item in node.ChildNodes)
                    triangles.AddRange(this.ProcessNode(item, modelNode));
            }

            return triangles;
        }

        /// <summary>
        /// Method process Group.
        /// </summary>
        /// <param name="obj"></param>
        private List<InternalTriangle> ProcessGroup(FLTAbstractGroup group, ModelInternalNode modelNode)
        {            
            ModelInternalNodeGroup newGroupNode = new ModelInternalNodeGroup();
            ModelInternalNode newNode = newGroupNode;            

            Matrix translation = Matrix.Identity;

            foreach (FLTAbstractBase item in group.AncillaryNodes)
            {
                if (item is FLTAbstractTranslate)
                {
                    translation = Matrix.CreateTranslation(
                        (Single)(item as FLTAbstractTranslate).DeltaToTranslate[0],
                        (Single)(item as FLTAbstractTranslate).DeltaToTranslate[2],
                        (Single)(item as FLTAbstractTranslate).DeltaToTranslate[1]);
                }
            }

            if (translation != Matrix.Identity)
            {
                ModelInternalNodeTransform newTransform = new ModelInternalNodeTransform();
                newTransform.Transform = translation;

                newGroupNode.Add(newTransform);

                newNode = newTransform;
            }
                        
            List<InternalTriangle> triangles = new List<InternalTriangle>();

            foreach (FLTAbstractBase item in group.ChildNodes)
                triangles.AddRange(this.ProcessNode(item, newNode));

            if (triangles.Count != 0)
                newGroupNode.AddRange(this.CreateDrawCallNodes(triangles));

            if (newGroupNode.Count > 1)
                modelNode.Add(newGroupNode);
            else if (newGroupNode.Count == 1)
                modelNode.Add(newGroupNode[0]);

            return new List<InternalTriangle>();
        }

        /// <summary>
        /// Method process object.
        /// </summary>
        /// <param name="obj"></param>
        private List<InternalTriangle> ProcessLOD(FLTAbstractLevelOfDetail lod, ModelInternalNode modelNode)
        {
            ModelInternalNodeLevelOfDetail newLodNode = new ModelInternalNodeLevelOfDetail();

            newLodNode.SwitchInDistance = (Single)(lod.SwitchInDistance * lod.SwitchInDistance);
            newLodNode.SwitchOutDistance = (Single)(lod.SwitchOutDistance * lod.SwitchOutDistance);
            newLodNode.CenterPoint = new Vector3((Single)lod.CenterCoordinateXOfLOD, 
                (Single)lod.CenterCoordinateZOfLOD, (Single)lod.CenterCoordinateYOfLOD);

            modelNode.Add(newLodNode);

            List<InternalTriangle> triangles = new List<InternalTriangle>();

            foreach (FLTAbstractBase item in lod.AncillaryNodes)
                triangles.AddRange(this.ProcessNode(item, newLodNode));

            foreach (FLTAbstractBase item in lod.ChildNodes)
                triangles.AddRange(this.ProcessNode(item, newLodNode));

            if (triangles.Count != 0)
                newLodNode.AddRange(this.CreateDrawCallNodes(triangles));

            return new List<InternalTriangle>();
        }

        /// <summary>
        /// Method process object.
        /// </summary>
        /// <param name="obj"></param>
        private List<InternalTriangle> ProcessExternalRef(FLTAbstractExternalReference extRef, ModelInternalNode modelNode)
        {
            ModelInternalNodeExternalReference newExtRefNode = new ModelInternalNodeExternalReference();

            newExtRefNode.ModelName = Path.GetFileNameWithoutExtension(extRef.ASCIIPath);
            
            
            modelNode.Add(newExtRefNode);

            List<InternalTriangle> triangles = new List<InternalTriangle>();

            newExtRefNode.Transform = Matrix.Identity;

            Matrix zToYMatrix = new Matrix(
                1, 0, 0, 1, 
                0, 0, 1, 1, 
                0, 1, 0, 1, 
                0, 0, 0, 1);

            foreach (FLTAbstractBase item in extRef.AncillaryNodes)
            {
                if (item is FLTAbstractGeneralMatrix)
                {
                    FLTAbstractGeneralMatrix matrix = item as FLTAbstractGeneralMatrix;

                    newExtRefNode.Transform = new Matrix(
                        matrix.Matrix[0], matrix.Matrix[1], matrix.Matrix[2], matrix.Matrix[3],
                        matrix.Matrix[4], matrix.Matrix[5], matrix.Matrix[6], matrix.Matrix[7],
                        matrix.Matrix[8], matrix.Matrix[9], matrix.Matrix[10], matrix.Matrix[11],
                        matrix.Matrix[12], matrix.Matrix[13], matrix.Matrix[14], matrix.Matrix[15]) *zToYMatrix;

                    newExtRefNode.Transform = Matrix.CreateTranslation(matrix.Matrix[12], matrix.Matrix[14], matrix.Matrix[13]);
                }
            }

            foreach (FLTAbstractBase item in extRef.ChildNodes)
                triangles.AddRange(this.ProcessNode(item, newExtRefNode));

            if (triangles.Count != 0)
                modelNode.AddRange(this.CreateDrawCallNodes(triangles));

            return new List<InternalTriangle>();
        }

        /// <summary>
        /// Method process object.
        /// </summary>
        /// <param name="obj"></param>
        private List<InternalTriangle> ProcessSwitch(FLTAbstractSwitch switchNode, ModelInternalNode modelNode)
        {
            ModelInternalNodeSwitch newSwitchNode = new ModelInternalNodeSwitch();

            newSwitchNode.CurrentMask = switchNode.CurrentMask;
            newSwitchNode.Masks = switchNode.MaskWords;

            for (int i = 0; i < newSwitchNode.Masks.Length; i++)
                Array.Reverse(newSwitchNode.Masks[i]);

            modelNode.Add(newSwitchNode);

            List<InternalTriangle> triangles = new List<InternalTriangle>();

            foreach (FLTAbstractBase item in switchNode.AncillaryNodes)
                triangles.AddRange(this.ProcessNode(item, newSwitchNode));

            foreach (FLTAbstractBase item in switchNode.ChildNodes)
                triangles.AddRange(this.ProcessNode(item, newSwitchNode));

            if (triangles.Count != 0)
                newSwitchNode.AddRange(this.CreateDrawCallNodes(triangles));

            return new List<InternalTriangle>();
        }

        /// <summary>
        /// Method process object.
        /// </summary>
        /// <param name="obj"></param>
        private List<InternalTriangle> ProcessObject(FLTAbstractObject obj, ModelInternalNode modelNode)
        {
            List<InternalTriangle> triangles = new List<InternalTriangle>();

            foreach (FLTAbstractBase item in obj.ChildNodes)
                if (item is FLTAbstractFace)
                    triangles.AddRange(this.ProcessFace(item as FLTAbstractFace));

            modelNode.AddRange(this.CreateDrawCallNodes(triangles));

            return new List<InternalTriangle>();
        }

        /// <summary>
        /// Method process object.
        /// </summary>
        /// <param name="obj"></param>
        private void ProcessInstanceDefinition(FLTAbstractInstanceDefinition iDef)
        {
            List<InternalTriangle> triangles = new List<InternalTriangle>();

            ModelInternalNode tempNode = new ModelInternalNode();

            foreach (FLTAbstractBase item in iDef.AncillaryNodes)
                triangles.AddRange(this.ProcessNode(item, tempNode));

            foreach (FLTAbstractBase item in iDef.ChildNodes)
                triangles.AddRange(this.ProcessNode(item, tempNode));

            tempNode.AddRange(this.CreateDrawCallNodes(triangles));
            
            this.Instances.Add(iDef.InstanceDefinitionNumber, tempNode);
        }

        /// <summary>
        /// Method process object.
        /// </summary>
        /// <param name="obj"></param>
        private void ProcessInstanceReference(FLTAbstractInstanceReference iRef, ModelInternalNode modelNode)
        {
            modelNode.AddRange(this.Instances[iRef.InstanceDefinitionNumber]);
        }

        /// <summary>
        /// Method process object.
        /// </summary>
        /// <param name="obj"></param>
        private List<InternalTriangle> ProcessDOF(FLTAbstractDegreeOfFreedom dof, ModelInternalNode modelNode)
        {
            ModelInternalNodeDOF newDOF = new ModelInternalNodeDOF();
            modelNode.Add(newDOF);

            newDOF.Transform = Matrix.CreateTranslation(
                (Single)dof.OriginOfDOFLocalCoordSystem[0],
                (Single)dof.OriginOfDOFLocalCoordSystem[2],
                (Single)dof.OriginOfDOFLocalCoordSystem[1]);

            List<InternalTriangle> triangles = new List<InternalTriangle>();

            foreach (FLTAbstractBase item in dof.AncillaryNodes)
                triangles.AddRange(this.ProcessNode(item, newDOF));

            foreach (FLTAbstractBase item in dof.ChildNodes)
                triangles.AddRange(this.ProcessNode(item, newDOF));

            if (triangles.Count != 0)
                newDOF.AddRange(this.CreateDrawCallNodes(triangles));

            return new List<InternalTriangle>();
        }

        /// <summary>
        /// Method creates draw call nodes.
        /// </summary>
        /// <param name="triangles"></param>
        /// <returns></returns>
        private IEnumerable<ModelInternalNodeGeometry> CreateDrawCallNodes(List<InternalTriangle> triangles)
        {
            Dictionary<ModelMaterialParameters, List<InternalTriangle>> materials =
                new Dictionary<ModelMaterialParameters, List<InternalTriangle>>(new ModelMaterialParametersEqualityComparer());

            foreach (InternalTriangle item in triangles)
            {
                if (!materials.ContainsKey(item.ModelMaterialParameters))
                    materials.Add(item.ModelMaterialParameters, new List<InternalTriangle>());

                materials[item.ModelMaterialParameters].Add(item);
            }

            foreach (KeyValuePair<ModelMaterialParameters, List<InternalTriangle>> item in materials)
            {
                ModelInternalNodeGeometry newGeometry = new ModelInternalNodeGeometry();

                newGeometry.MaterialParameters = item.Key;

                Dictionary<IVertexType, int> vertexIndices = new Dictionary<IVertexType, int>();

                List<IVertexType> vbo = new List<IVertexType>();
                List<Int32> ibo = new List<int>();

                List<Vector3> vertices = new List<Vector3>();

                foreach (InternalTriangle triangle in item.Value)
                {
                    if (!vertexIndices.ContainsKey(triangle.Vertex1))
                    {
                        vertexIndices.Add(triangle.Vertex1, vbo.Count);
                        vbo.Add(triangle.Vertex1);
                    }

                    ibo.Add(vertexIndices[triangle.Vertex1]);

                    if (!vertexIndices.ContainsKey(triangle.Vertex2))
                    {
                        vertexIndices.Add(triangle.Vertex2, vbo.Count);
                        vbo.Add(triangle.Vertex2);
                    }

                    ibo.Add(vertexIndices[triangle.Vertex2]);

                    if (!vertexIndices.ContainsKey(triangle.Vertex3))
                    {
                        vertexIndices.Add(triangle.Vertex3, vbo.Count);
                        vbo.Add(triangle.Vertex3);
                    }

                    ibo.Add(vertexIndices[triangle.Vertex3]);

                    vertices.Add(triangle.Vertex1Vector3);
                    vertices.Add(triangle.Vertex2Vector3);
                    vertices.Add(triangle.Vertex3Vector3);
                }

                VertexBufferContent vboContent = this.CreateVertexBuffer(vbo);

                newGeometry.NumVertices = vbo.Count;
                newGeometry.PrimitiveCount = ibo.Count / 3;
                newGeometry.PrimitiveType = Microsoft.Xna.Framework.Graphics.PrimitiveType.TriangleList;

                VertexElement[] elements = new VertexElement[vboContent.VertexDeclaration.VertexElements.Count];
                for (int i = 0; i < vboContent.VertexDeclaration.VertexElements.Count; i++)
                    elements[i] = vboContent.VertexDeclaration.VertexElements[i];

                if (!this.VDBuffers.ContainsKey(vboContent.VertexDeclaration))
                {
                    this.VDBuffers.Add(vboContent.VertexDeclaration, this.VDBuffers.Count);
                    this.Buffers.Add(new ModelInternalBuffer());
                    this.Buffers[this.Buffers.Count - 1].VertexElements = elements;
                    this.Buffers[this.Buffers.Count - 1].ElementSize = IndexElementSize.ThirtyTwoBits;
                    this.Buffers[this.Buffers.Count - 1].VertexStride = vboContent.VertexDeclaration.VertexStride;
                }

                newGeometry.BuffersIndex = this.VDBuffers[vboContent.VertexDeclaration];

                newGeometry.BaseVertex = this.Buffers[newGeometry.BuffersIndex].VerticesCount;

                this.Buffers[newGeometry.BuffersIndex].VerticesCount += vbo.Count;
                this.Buffers[newGeometry.BuffersIndex].VertexBufferContent.AddRange(vboContent.VertexData);

                newGeometry.StartIndex = this.Buffers[newGeometry.BuffersIndex].IndicesCount;

                newGeometry.BoundingSphere = BoundingSphere.CreateFromPoints(vertices);

                foreach (int itemIbo in ibo)
                    this.Buffers[newGeometry.BuffersIndex].IndexBufferContent.AddRange(BitConverter.GetBytes((int)itemIbo));

                this.Buffers[newGeometry.BuffersIndex].IndicesCount += ibo.Count;

                yield return newGeometry;
            }
        }

        /// <summary>
        /// Method process face node.
        /// </summary>
        /// <param name="face"></param>
        private IEnumerable<InternalTriangle> ProcessFace(FLTAbstractFace face)
        {
            List<InternalTriangle> triangles = new List<InternalTriangle>();

            foreach (FLTAbstractBase item in face.ChildNodes)
                if (item is FLTAbstractVertexList)
                    triangles.AddRange(this.ProcessVertexList(item as FLTAbstractVertexList, face));

            return triangles;
        }

        /// <summary>
        /// Method process vertex list.
        /// </summary>
        /// <param name="vertexList"></param>
        private IEnumerable<InternalTriangle> ProcessVertexList(FLTAbstractVertexList vertexList, FLTAbstractFace face)
        {
            for (int i = 0; i < vertexList.ByteOffset.Length - 2; i++)
            {
                InternalTriangle newTriangle = new InternalTriangle();

                newTriangle.Vertex1 = this.GetVertex(vertexList.ByteOffset[0], out newTriangle.Vertex1Vector3);
                newTriangle.Vertex2 = this.GetVertex(vertexList.ByteOffset[i + 1], out newTriangle.Vertex2Vector3);
                newTriangle.Vertex3 = this.GetVertex(vertexList.ByteOffset[i + 2], out newTriangle.Vertex3Vector3);

                newTriangle.ModelMaterialParameters = new ModelMaterialParameters();
                newTriangle.ModelMaterialParameters.Name = "MaterialNormalMap";

                if (face.TexturePatternIndex != -1)
                    newTriangle.ModelMaterialParameters.Textures.Add("Diffuse",
                        Path.GetFileNameWithoutExtension(this.TexturePalettes[face.TexturePatternIndex].FileName));

                yield return newTriangle;
            }
        }

        /// <summary>
        /// Method returns vertex.
        /// </summary>
        /// <param name="byteOffset"></param>
        /// <returns></returns>
        public IVertexType GetVertex(int byteOffset, out Vector3 vector3Vertex)
        {
            if (this.VertexPalette[byteOffset] is FLTAbstractVertexWithColorAndUV)
            {
                vector3Vertex = new Vector3(
                    (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndUV).VertexCoordinate[0],
                    (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndUV).VertexCoordinate[2],
                    (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndUV).VertexCoordinate[1]);

                return new VertexPositionNormalTexture(
                    new Vector3(
                        (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndUV).VertexCoordinate[0],
                        (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndUV).VertexCoordinate[2],
                        (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndUV).VertexCoordinate[1]),
                    Vector3.Zero,
                    new Vector2(
                        (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndUV).TextureCoordinate[0],
                        (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndUV).TextureCoordinate[1]));
            }
            else
                if (this.VertexPalette[byteOffset] is FLTAbstractVertexWithColorNormalAndUV)
                {
                    vector3Vertex = new Vector3(
                        (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).VertexCoordinate[0],
                        (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).VertexCoordinate[2],
                        (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).VertexCoordinate[1]);

                    return new VertexPositionNormalTexture(
                        new Vector3(
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).VertexCoordinate[0],
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).VertexCoordinate[2],
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).VertexCoordinate[1]),
                        new Vector3(
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).VertexNormal[0],
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).VertexNormal[2],
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).VertexNormal[1]),
                        new Vector2(
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).TextureCoordinate[0],
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorNormalAndUV).TextureCoordinate[1]));
                }
                else
                    if (this.VertexPalette[byteOffset] is FLTAbstractVertexWithColorAndNormal)
                    {
                        vector3Vertex = new Vector3(
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndNormal).VertexCoordinate[0],
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndNormal).VertexCoordinate[2],
                            (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndNormal).VertexCoordinate[1]);

                        return new VertexPositionNormalTexture(
                            new Vector3(
                                (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndNormal).VertexCoordinate[0],
                                (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndNormal).VertexCoordinate[2],
                                (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndNormal).VertexCoordinate[1]),
                            new Vector3(
                                (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndNormal).VertexNormal[0],
                                (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndNormal).VertexNormal[2],
                                (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColorAndNormal).VertexNormal[1]),
                            Vector2.Zero);
                    }
                    else
                        if (this.VertexPalette[byteOffset] is FLTAbstractVertexWithColor)
                        {
                            vector3Vertex = new Vector3(
                                (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColor).VertexCoordinate[0],
                                (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColor).VertexCoordinate[2],
                                (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColor).VertexCoordinate[1]);

                            return new VertexPositionNormalTexture(
                                new Vector3(
                                    (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColor).VertexCoordinate[0],
                                    (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColor).VertexCoordinate[2],
                                    (Single)(this.VertexPalette[byteOffset] as FLTAbstractVertexWithColor).VertexCoordinate[1]),
                                Vector3.Zero,
                                Vector2.Zero);
                        }
                        else
                        {
                            using (StreamWriter sw = new StreamWriter(@"C:\Marek\problem.txt"))
                            {
                                sw.Write(this.VertexPalette[byteOffset].ToString());
                            }
                        }
            vector3Vertex = Vector3.Zero;

            return new VertexPositionColor();
        }

        /// <summary>
        /// Method process vertex palette.
        /// </summary>
        /// <param name="vertexPalette"></param>
        private void ProcessVertexPalette(FLTAbstractVertexPalette vertexPalette)
        {
            this.VertexPalette = vertexPalette;

            //Dictionary<Type, List<FLTAbstractBase>> vertexBuffers = new Dictionary<Type,List<FLTAbstractBase>>();

            //foreach (KeyValuePair<int, FLTAbstractBase> item in vertexPalette.Vertices)
            //{
            //    if (!vertexBuffers.ContainsKey(item.Value.GetType()))
            //        vertexBuffers.Add(item.Value.GetType(), new List<FLTAbstractBase>());

            //    vertexBuffers[item.Value.GetType()].Add(item.Value);
            //}

            //foreach (KeyValuePair<Type, List<FLTAbstractBase>> item in vertexBuffers)
            //{
            //    ModelInternalBuffer newInternalBuffer = new ModelInternalBuffer();
            //    newInternalBuffer.ElementSize = IndexElementSize.SixteenBits;
            //    newInternalBuffer.VerticesCount = item.Value.Count;
            //    newInternalBuffer.VertexStride = null;
            //    newInternalBuffer.VertexElements = new VertexElement[] { 
            //            new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
            //            new VertexElement(0, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)};
            //    newInternalBuffer.VertexBufferContent = new List<Byte>();

            //    for (int i = 0; i < item.Value.Count; i++)
            //    {
            //        if (item.Value[i] is FLTAbstractVertexWithColorAndUV)
            //        {
            //            newInternalBuffer.VertexBufferContent.AddRange(BitConverter.GetBytes(
            //                (Single)(item.Value[i] as FLTAbstractVertexWithColorAndUV).VertexCoordinate[0]));
            //            newInternalBuffer.VertexBufferContent.AddRange(BitConverter.GetBytes(
            //                (Single)(item.Value[i] as FLTAbstractVertexWithColorAndUV).VertexCoordinate[1]));
            //            newInternalBuffer.VertexBufferContent.AddRange(BitConverter.GetBytes(
            //                (Single)(item.Value[i] as FLTAbstractVertexWithColorAndUV).VertexCoordinate[2]));

            //            newInternalBuffer.VertexBufferContent.AddRange(BitConverter.GetBytes(
            //                (Single)(item.Value[i] as FLTAbstractVertexWithColorAndUV).TextureCoordinate[0]));
            //            newInternalBuffer.VertexBufferContent.AddRange(BitConverter.GetBytes(
            //                (Single)(item.Value[i] as FLTAbstractVertexWithColorAndUV).TextureCoordinate[1]));
            //        }
            //    }
            //}
        }

        /// <summary>
        /// Method creates VertexBufferContent.
        /// </summary>
        /// <param name="vertices"></param>
        /// <returns></returns>
        private VertexBufferContent CreateVertexBuffer(List<IVertexType> vertices)
        {
            VertexBufferContent content = new VertexBufferContent();

            content.Write(0, 32, vertices[0].GetType(), vertices);
            content.VertexDeclaration = new VertexDeclarationContent();

            foreach (VertexElement item in vertices[0].VertexDeclaration.GetVertexElements())
                content.VertexDeclaration.VertexElements.Add(item);

            content.VertexDeclaration.VertexStride = vertices[0].VertexDeclaration.VertexStride;
            
            return content;
        }

        /// <summary>
        /// Method computes bounding sphere.
        /// </summary>
        /// <param name="node"></param>
        private BoundingSphere ComputeBoundingSphere(ModelInternalNode node, Matrix transformation)
        {
            Matrix transform = transformation;

            if (node is ModelInternalNodeTransform)
                transform = (node as ModelInternalNodeTransform).Transform * transformation;

            if (node is ModelInternalNodeGeometry)
                return node.BoundingSphere.Transform(transform);

            List<BoundingSphere> boundings = new List<BoundingSphere>();

            foreach (ModelInternalNode item in node)
            {
                BoundingSphere boundingSphere = this.ComputeBoundingSphere(item, transform);

                if (boundingSphere != new BoundingSphere())
                    boundings.Add(boundingSphere);                
            }

            if (boundings.Count > 0)
            {
                node.BoundingSphere = boundings[0];

                for (int i = 1; i < boundings.Count; i++)
                    node.BoundingSphere = BoundingSphere.CreateMerged(node.BoundingSphere, boundings[i]);

                node.BoundingSphere = node.BoundingSphere.Transform(transform);
            }
            else
                node.BoundingSphere = new BoundingSphere();

            return node.BoundingSphere;
        }

        /// <summary>
        /// Method writes file in Ascii code.
        /// </summary>
        /// <param name="rootNode"></param>
        private void Ascii(FLTAbstractBase rootNode, string filename)
        {
            using (StreamWriter sw = new StreamWriter(filename))
                this.Ascii(rootNode, sw);
        }

        /// <summary>
        /// Method writes file in Ascii code.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="sw"></param>
        private void Ascii(FLTAbstractBase node, StreamWriter sw)
        {
            sw.WriteLine(node.ToString());

            foreach (FLTAbstractBase item in node.AncillaryNodes)
                this.Ascii(item, sw);

            foreach (FLTAbstractBase item in node.ChildNodes)
                this.Ascii(item, sw);
        }
    }
}
#endif